/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JLabel;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.model.Utils;
import ro.dta.idbi.patterns.ColGenerRev;
import ro.dta.idbi.ui.sql.SqlColumnCell;
import ro.dta.idbi.ui.sql.SqlForeignKeyCell;
import ro.dta.idbi.ui.sql.SqlGraph;
import ro.dta.idbi.ui.sql.SqlTableCell;
import uk.ac.ic.doc.automed.reps.Schema;

import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;

/**
 * UI for the reverse transformation of the <i>mandatory column and total
 * generalisation pattern</i>
 * 
 * @author Tudor Dobrila
 * 
 */
public class ColGenerRevDialog extends AbstractPatternDialog {

	private static final long serialVersionUID = 1104403624772689795L;
	private static final Logger LOGGER = Logger
			.getLogger("ro.dta.idbi.ui.patterns.ColGenerRevDialog");
	
	/**
	 * Check if the current pattern can be applied over a given set of objects
	 * 
	 * Used to check if the items selected by the user in the UI can be used to
	 * apply this transformation pattern over them
	 * 
	 * @param schema
	 *            Schema in the context of which to check the validity of the
	 *            array of objects
	 * @param objects
	 *            Set of objects
	 * @return True if pattern can be applied over set of objects, false
	 *         otherwise
	 */
	public static boolean isValid(final Schema schema, final Object[] objects) {
		if (objects.length < 2) {
			return false;
		}
		
		for (Object o : objects) {
			if (!(o instanceof ISqlTable)) {
				return false;
			}
		}

		// Check that there is a parent that is a child of the other tables
		final ISqlTable[] tables = new ISqlTable[objects.length];
		System.arraycopy(objects, 0, tables, 0, objects.length);
		final ISqlTable parent = Utils.getCommonSupertype(schema, tables);

		if (parent == null) {
			return false;
		}

		// Check that the child tables only have the columns in the PK of the
		// parent table as columns
		List<ISqlColumn> cols = parent.getColumns();
		List<String> pks = new ArrayList<String>();
		for (ISqlColumn c : cols) {
			if (c.isPK()) {
				pks.add(c.getValue().toString());
			}
		}

		for (int i = 0; i < tables.length; i++) {
			if (tables[i] == parent) {
				continue;
			}

			cols = tables[i].getColumns();
			if (cols.size() != pks.size()) {
				return false;
			}

			for (ISqlColumn c : cols) {
				if (!pks.contains(c.getValue().toString())) {
					return false;
				}
			}
		}

		return true;
	}
	
	/**
	 * Parent table
	 */
	private ISqlTable parent;

	/**
	 * Reverse of mandatory column and total generalisation dialog class
	 * constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of this dialog
	 * @param schema
	 *            Schema over which the transformation is applied
	 * @param element
	 *            Sql construct(s) over which the transformation is applied
	 * @param beforeLabel
	 *            Label for the before diagram
	 * @param afterLabel
	 *            Label for the transformed diagram
	 */
	public ColGenerRevDialog(IMainFrame parentFrame, Schema schema, ISqlConstruct[] element,
			JLabel beforeLabel, JLabel afterLabel) {
		super(parentFrame, schema, element, beforeLabel, afterLabel);

		if (!isValid(schema, element)) {
			throw new IllegalArgumentException(
					"This pattern can't be applied to the given elements");
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.ui.patterns.AbstractTransDialog#createOriginalDiagram()
	 */
	@Override
	protected mxGraphComponent createOriginalDiagram() {
		initialise();

		final SqlGraph graph = new SqlGraph();
		graph.getModel().beginUpdate();

		try {
			SqlTableCell[] newTables = new SqlTableCell[element.length];
			SqlTableCell parentTbl = null;
			for (int i = 0; i < element.length; i++) {
				final SqlTableCell crtTbl = (SqlTableCell) element[i];
				final SqlTableCell newTable = (SqlTableCell) crtTbl.clone();
				final mxGeometry newGeometry = new mxGeometry(i * 170 + 5, 5, element[0]
						.getGeometry().getWidth(), element[0].getGeometry().getHeight());
				newTable.setGeometry(newGeometry);
				if (element[i] != parent) {
					newTable.setStyle("SEL_TABLE");
				}
				newTable.layout(graph);
				newTables[i] = newTable;

				if (element[i] == parent) {
					parentTbl = newTable;
				}
			}

			for (int i = 0; i < element.length; i++) {
				if (element[i] != parent) {
					final SqlForeignKeyCell fk = new SqlForeignKeyCell(null, null, "", "", newTables[i],
							parentTbl, "FK_EDGE");
					graph.addCell(fk, graph.getDefaultParent());
				}
			}
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			final mxHierarchicalLayout layout = new mxHierarchicalLayout(graph);
			layout.execute(graph.getDefaultParent());
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return new mxGraphComponent(graph);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.ui.patterns.AbstractTransDialog#createTransformedDiagram()
	 */
	@Override
	protected mxGraphComponent createTransformedDiagram() {
		final SqlGraph graph = new EditableGraph();
		graph.getModel().beginUpdate();
		SqlColumnCell newCol = null;

		try {
			final SqlTableCell crtTbl = (SqlTableCell) parent;
			final SqlTableCell newTable = (SqlTableCell) crtTbl.clone();
			final mxGeometry newGeometry = new mxGeometry(5, 5, parent.getGeometry().getWidth(),
					parent.getGeometry().getHeight());
			newTable.setGeometry(newGeometry);
			newCol = new SqlColumnCell(null, null, "", "column", new mxGeometry(0, 0, 150, 25),
					"SEL_COL");
			newTable.addColumn(newCol);
			newTable.relayout(graph);
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			graph.getModel().endUpdate();
		}

		final mxGraphComponent gComp = new mxGraphComponent(graph);
		gComp.addListener(mxEvent.LABEL_CHANGED, new mxIEventListener() {

			@Override
			public void invoke(final Object arg0, final mxEventObject arg1) {
				SqlColumnCell col = (SqlColumnCell) arg1.getProperty("cell");
				updateMacro(col.getValue().toString());
			}
		});
		gComp.setEnterStopsCellEditing(true);
		if (newCol != null) {
			gComp.startEditingAtCell(newCol);
		}

		return gComp;
	}

	/**
	 * Initialise transformation dialog
	 */
	private void initialise() {
		ISqlTable[] tables = new ISqlTable[element.length];
		System.arraycopy(element, 0, tables, 0, element.length);
		parent = Utils.getCommonSupertype(schema, tables);
		if (parent == null) {
			throw new IllegalArgumentException(
					"This pattern can't be applied to the given elements");

		}

		updateMacro("column");
	}

	/**
	 * Update the macro command
	 */
	private void updateMacro(String colName) {
		final StringBuilder cmd = new StringBuilder(ColGenerRev.COMMAND).append(" (");
		final StringBuilder vals = new StringBuilder("{");
		cmd.append("<<").append(parent.getValue()).append(",").append(colName).append(">>");
		for (ISqlConstruct e : element) {
			if (e != parent) {
				cmd.append(", <<").append(e.getValue()).append(">>");
				vals.append("'").append(e.getValue()).append("', ");
			}
		}
		vals.setLength(vals.length() - 2);
		vals.append("}");
		cmd.append(", ").append(vals);
		cmd.append(")");
		macroField.setText(cmd.toString());
	}

}
